package ke03;

import java.math.BigInteger;

public class Summe extends Thread {

	/*
	 * Jeder Thread k�mmert sich nur um die Aufsummierung eines bestimmten
	 * Teilintervalls aus der Gesamt-Summierung:
	 */
	private long von;
	private long bis;

	/* Um �berlaufe zu vermeiden, ben�tigen wir hier nat�rlich auch BigInteger */
	private BigInteger ergebnis = BigInteger.ZERO;

	/*
	 * Flag, um der berechneSumme()-Methode mitzuteilen, ob die Berechnung
	 * vollendet ist
	 */
	private boolean fertig = false;

	public Summe(long von, long bis) {
		this.von = von;
		this.bis = bis;
	}

	@Override
	public void run() {
		/* Wie gehabt ein einfaches iteratives Aufsummieren des Ergebnisses */
		while (von >= bis) {
			ergebnis = ergebnis.add(new BigInteger(von + ""));
			von--;
		}

		/* Setze Flag */
		fertig = true;

		/* Benachrichtige berechneSumme() dar�ber, dass der Thread fertig ist */
		synchronized (this) {
			notifyAll();
		}
	}

	public boolean isFertig() {
		return fertig;
	}

	public BigInteger getErgebnis() {
		return ergebnis;
	}

	public static BigInteger berechneSumme(long zahl, int threads) {

		/* Pr�fe Argumente... */
		if (zahl < 1) {
			throw new IllegalArgumentException("Ung�ltige Zahl: " + zahl);
		}
		if (threads < 1) {
			throw new IllegalArgumentException("Ung�ltige Thread-Anzahl: "
					+ threads);
		}
		if (threads > zahl) {
			throw new IllegalArgumentException(
					"Anzahl der Threads �bersteigt den Wert der Zahl!");
		}

		/*
		 * Berechne die Intervall-Gr��e, die jeder Thread aufsummieren muss, um
		 * den Berechnungs-Aufwand fair zwischen allen Threads zu verteilen:
		 */
		long intervall = zahl / threads;
		/*
		 * Beachte, dass die Division nicht aufgehen muss. Den Rest d�rfen wir
		 * nicht vernachl�ssigen:
		 */
		long rest = zahl % threads;

		/*
		 * Erzeuge die Instanzen f�r die einzelnen Threads, wobei jeder Thread
		 * ein Intervall aus der n�tigen Gesamt-Summierung erh�lt:
		 */
		Summe[] threadArray = new Summe[threads];
		long von = 0;
		long bis = 0;
		for (int i = 0; i < threads; i++) {
			if (i == 0) {
				/*
				 * Der erste Thread berechnet auch das, was bei der Aufteilung
				 * eventuell �brig bleibt, weil es nicht aufgeht
				 */
				von = zahl;
				bis = zahl - intervall + 1;
				bis -= rest;
			} else {
				/*
				 * Alle anderen berechnen das vorher berechnete Intervall - mit
				 * jeweils unterschiedlichem Anfangs- und Endwert
				 */
				von = bis - 1;
				bis = von - intervall + 1;
			}
			Summe sum = new Summe(von, bis);
			threadArray[i] = sum;
		}

		/* Starte Threads... */
		for (Summe sum : threadArray) {
			new Thread(sum).start();
		}

		/* Warte, bis alle Threads ihre Berechnungen durchgef�hrt haben */
		for (Summe sum : threadArray) {
			synchronized (sum) {
				while (!sum.isFertig()) {
					try {
						sum.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}

		/* Summiere die einzelnen Ergebnisse, um das Endergebnis zu erhalten */
		BigInteger endErgebnis = BigInteger.ZERO;
		for (Summe sum : threadArray) {
			endErgebnis = endErgebnis.add(sum.getErgebnis());
		}
		return endErgebnis;
	}

}
